#pragma once

#include <jsoncpp/json/json.h>
#include <signal.h>

#include "complier.hpp"
#include "runner.hpp"
#include "../comm/util.hpp"
#include "../comm/log.hpp"

namespace ns_compile_run
{
    /*

    输入:
        code : 用户提交的代码
        input: 用户提交代码对应的输入

    输出:
        status:状态码
        reason: 请求结果

    选填:
        stdout:程序运行的输出
        stderr:程序运行完的错误
    */

    class Compile_Run
    {

    public:
        // 将信号转换为报错的原因
        static std::string CodeToDesc(int run_result, const std::string file_name)
        {
            /*
            code > 0 : 进程收到信号崩溃
            code < 0 : 服务器内部错误 (非运行报错,编译报错)
            code == 0 : 成功运行

            */

            std::string desc;

            switch (run_result)
            {
            case 0:
                desc = "code running success!";
                break;
            case -1:
                desc = "Submit as empty!";
                break;
            case -2:
                desc = "未知错误";
                break;
            case -3: // 代码编译错误,需要返回 g++ 的错误编译信息,
                // desc = "代码编译错误";             // Compile_Error 接口读取编译错误的 g++ 信息
                ns_util::FileUtil::ReadFile(ns_util::PathUtil::Compile_Error(file_name),&desc,true); // 保留文件的 换行符    
                // ReadFile 的返回值是 bool
                
                break;
            case SIGABRT: // 6号信号
                desc = "Running memory exceeds the range!";
                break;
            case SIGXCPU: // 24 号
                desc = "Code Run Timeout";
                break;
            case SIGFPE: // 8
                desc = "浮点数溢出";
                break;
            default:
                desc = "degug Unknow: " + std::to_string(run_result);
                break;
            }

            return desc;
        }

        static void RemoveTempFile(const std::string &file_name)
        {
            // 清理文件的个数是不确定的
            std::string src = ns_util::PathUtil::Src(file_name);
            if(ns_util::FileUtil::IsFileExists(src) )
            unlink(src.c_str());

            std::string compiler_error = ns_util::PathUtil::Compile_Error(file_name);
            if(ns_util::FileUtil::IsFileExists(compiler_error) )
            unlink(compiler_error.c_str());

            std::string exe = ns_util::PathUtil::Exe(file_name);
            if(ns_util::FileUtil::IsFileExists(exe) )
            unlink(exe.c_str());

            std::string _stderr = ns_util::PathUtil::Stderr(file_name);
            if(ns_util::FileUtil::IsFileExists(_stderr) )
            unlink(_stderr.c_str());

            std::string _stdin = ns_util::PathUtil::Stdin(file_name);
            if(ns_util::FileUtil::IsFileExists(_stdin) )
            unlink(_stdin.c_str());

            std::string _stdout = ns_util::PathUtil::Stdout(file_name);
            if(ns_util::FileUtil::IsFileExists(_stdout) )
            unlink(_stdout.c_str());

        }

        static void Start(const std::string &in_json, std::string *out_json)
        {
            /*
                in_json: {"code": #include..., "input:" ,"cpu_limit":1 }
                out_json: {"status":"0", "reason": ...  "stdout":   "stderr:" }
             */

            //  in_json : 客户端上传的序列化内容
            Json::Value in_value;
            Json::Reader reader;

            // 将客户端内容反序列化 并将结果放入 Json::Value in_value 中
            reader.parse(in_json, in_value);

            std::string code = in_value["code"].asString();
            std::string input = in_value["input"].asString();

            int cpu_limit = in_value["cpu_limit"].asInt();
            int mem_limit = in_value["mem_limit"].asInt();

            // 形成唯一的文件名  :: 毫秒级时间戳 + 原子性唯一递增
            std::string file_name = ns_util::FileUtil::Uniqe_FileName();

            Json::Value out_value;  // 需要返回给客户端的内容

            int status_code = 0;
            int run_result = 0;

            if (code.size() == 0)
            {
                // 客户端提交的代码为空
                status_code = -1;
                // out_value["reason"] = "User commiting is empty!";
                goto END;
            }

            //  将 用户提交的代码 写入临时文件
            if (!ns_util::FileUtil::WriteFile(ns_util::PathUtil::Src(file_name), code))
            {
                // 写入失败
                status_code = -2; // 未知错误
                // out_value["reason"] = "代码发生未知错误";
                // return;
                ns_log::lg.LogMessage(ns_log::Fatal,"服务器内部错误,文件写入失败\n");
                goto END;
            }

            // 编译代码
            if (!ns_complier::Complier::Compile(file_name))
            {
                // 编译失败
                status_code = -3; // 代码编译产生错误
                // out_value["reason"] = ns_util::FileUtil::ReadFile(ns_util::PathUtil::Compile_Error(file_name));
                // return;
                ns_log::lg.LogMessage(ns_log::Info,"编译失败\n");
                goto END;
            }

            // 运行代码
            run_result = ns_runner::Runner::Run(file_name, cpu_limit, mem_limit);
            if (run_result < 0)
            {
                // 服务器内部出错
                status_code = -2; // 未知错误
                // out_value["reason"] = "代码发生未知错误";
                // return;
                ns_log::lg.LogMessage(ns_log::Fatal,"服务器内部错误,运行失败\n");
            }
            else if (run_result > 0)
            {
                // 客户代码出错
                status_code = run_result; // 运行时错误  return status & 0x7F;
                // out_value["reason"] = SignoToDesc(run_result); // 将信号转换为报错的原因
            }
            else
            {
                //  _code == 0
                // 运行成功
                status_code = 0;
            }

            {

            END:
                out_value["status"] = status_code;
                // 这里需要传入文件名,如果是编译错误,需要读取 g++ 编译的错误信息
                out_value["reason"] = CodeToDesc(status_code, file_name);

                if (status_code == 0)
                {
                    // 以上运行过程全都成功
                    std::string _stdout;
                    ns_util::FileUtil::ReadFile(ns_util::PathUtil::Stdout(file_name), &_stdout, true); // 保留文件中的 \n
                    out_value["stdout"] = _stdout;

                    std::string _stderr;
                    ns_util::FileUtil::ReadFile(ns_util::PathUtil::Stderr(file_name), &_stderr, true);
                    out_value["stderr"] = _stderr;

                    ns_log::lg.LogMessage(ns_log::Info,"编译运行成功\n");
                }

                // 对 out_value 进行序列化
                Json::StyledWriter _writer;
                *out_json = _writer.write(out_value);

                RemoveTempFile(file_name); // 删除临时文件 
            }
        }
    };
}